﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TransactionManager.cs" company="Concept Architecture">
//   (c) 2010 Muhammad Umer Farooq
// </copyright>
// <summary>
//   Defines the  interface.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Hisaab.Business
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using Common;
    using Data.Interfaces;
    using Entities;
    using Interfaces;

    using Microsoft.Practices.ServiceLocation;

    /// <summary>
    /// Defines the <seealso cref="Hisaab.Business.TransactionManager"/> type.
    /// </summary>
    public class TransactionManager : ITransactionManager
    {
        /// <summary>
        /// Transaction repository
        /// </summary>
        private readonly ITransactionRepository _transactionRepository;

        /// <summary>
        /// Tag repository
        /// </summary>
        private readonly ITagRepository _tagRepository;

        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionManager"/> class.
        /// </summary>
        public TransactionManager()
            : this(ServiceLocator.Current.GetInstance<ITransactionRepository>(), ServiceLocator.Current.GetInstance<ITagRepository>())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionManager"/> class.
        /// </summary>
        /// <param name="transactionRepository">The transaction repository.</param>
        /// <param name="tagRepository">The tag repository</param>
        public TransactionManager(ITransactionRepository transactionRepository, ITagRepository tagRepository)
        {
            Contract.Requires<ArgumentNullException>(transactionRepository != null);
            _transactionRepository = transactionRepository;
            _tagRepository = tagRepository;
        }

        /// <summary>
        /// Gets the filtered.
        /// </summary>
        /// <param name="filter">The filter.</param>
        /// <param name="pageNo">The page no.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <returns>List of filtered items</returns>
        public IEnumerable<Transaction> List(Transaction filter, int? pageNo, int? pageSize)
        {
            return _transactionRepository.Query(filter.Map<Transaction, Data.Entities.Transaction>(), pageNo, pageSize)
                .OrderBy(x => x.Date)
                .ToList()
                .Select(x => x.Map<Data.Entities.Transaction, Transaction>());
        }

        /// <summary>
        /// Adds the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns>Id of transaction added</returns>
        public int Add(Transaction entity)
        {
            var mappedTransaction = entity.Map<Transaction, Data.Entities.Transaction>();

            if (entity.IsTransferTransaction)
            {
                var transaction = new Data.Entities.Transaction
                                      {
                                          Title = entity.Title,
                                          Amount = Math.Abs(entity.Amount),
                                          Account = entity.LinkedTransactionAccount,
                                          Date = entity.LinkedTransactionDate,
                                          Details = entity.Details,
                                          RelatedTransactionId = null
                                      };
                mappedTransaction.RelatedTransactionId = _transactionRepository.Add(transaction);

                if (mappedTransaction.Amount > 0)
                {
                    mappedTransaction.Amount = -mappedTransaction.Amount;
                }
            }

            var mainTransactionId = _transactionRepository.Add(mappedTransaction);
            if (entity.IsTransferTransaction)
            {
                var relatedTransaction = _transactionRepository.FindById(entity.RelatedTransactionId.Value);
                if (relatedTransaction != null)
                {
                    relatedTransaction.RelatedTransactionId = mainTransactionId;
                    _transactionRepository.Update(relatedTransaction);
                }
            }

            return mainTransactionId;
        }

        /// <summary>
        /// Updates the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Update(Transaction entity)
        {
            _transactionRepository.Update(entity.Map<Transaction, Data.Entities.Transaction>());
        }

        /// <summary>
        /// Deletes the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Delete(Transaction entity)
        {
            _transactionRepository.Delete(entity.Map<Transaction, Data.Entities.Transaction>());
        }

        /// <summary>
        /// Gets the by id.
        /// </summary>
        /// <param name="id">The transaction id.</param>
        /// <returns>Transaction matching id</returns>
        public Transaction GetById(int id)
        {
            return _transactionRepository.FindById(id).Map<Data.Entities.Transaction, Transaction>();
        }

        /// <summary>
        /// Gets transaction details by id.
        /// </summary>
        /// <param name="id">The transaction id.</param>
        /// <returns>Transaction matching id</returns>
        public Transaction GetTransactionDetails(int id)
        {
            var transaction = _transactionRepository.FindById(id).Map<Data.Entities.Transaction, Transaction>();
            transaction.Tags = _tagRepository.GetTagsForTransaction(id).Select(x => x.Map<Data.Entities.Tag, Tag>());
            return transaction;
        }

        /// <summary>
        /// Filtereds the list.
        /// </summary>
        /// <param name="filter">The filter.</param>
        /// <param name="filterAccountId">The filter account id.</param>
        /// <returns>
        /// List of transactions in filter
        /// </returns>
        public IEnumerable<Transaction> FilteredList(TransactionFilter filter, int? filterAccountId)
        {
            var result =
                _transactionRepository.QueryBetweenDates(
                    filterAccountId.HasValue ? new Data.Entities.Transaction { Account = filterAccountId.Value } : null,
                    filter.StartDate,
                    filter.EndDate,
                    (Data.Entities.SortByType)filter.SortBy,
                    (Data.Entities.OrderByType)filter.OrderBy,
                    x => (filter.IncludeExpanse && x.Amount < 0) || (filter.IncludeIncome && x.Amount > 0));

            return result.Select(x => x.Map<Data.Entities.Transaction, Transaction>());
        }

        /// <summary>
        /// Relates the transactions.
        /// </summary>
        /// <param name="sourceTransactionId">The source transaction id.</param>
        /// <param name="destinationTransactionId">The destination transaction id.</param>
        public void RelateTransactions(int sourceTransactionId, int destinationTransactionId)
        {
            var thisTransaction = _transactionRepository.FindById(sourceTransactionId);
            thisTransaction.RelatedTransactionId = destinationTransactionId;
            _transactionRepository.Update(thisTransaction);

            var otherTransaction = _transactionRepository.FindById(destinationTransactionId);
            otherTransaction.RelatedTransactionId = sourceTransactionId;
            _transactionRepository.Update(otherTransaction);
        }

        /// <summary>
        /// Filtereds the list by tag.
        /// </summary>
        /// <param name="filter">The transaction filter.</param>
        /// <param name="tagId">The tag id.</param>
        /// <returns>List of transactions in filter</returns>
        public IEnumerable<Transaction> FilteredListByTag(TransactionFilter filter, int? tagId)
        {
            var result =
                _transactionRepository.QueryBetweenDatesByTag(
                    tagId.Value,
                    filter.StartDate,
                    filter.EndDate,
                    (Data.Entities.SortByType)filter.SortBy,
                    (Data.Entities.OrderByType)filter.OrderBy,
                    x => (filter.IncludeExpanse && x.Amount < 0) || (filter.IncludeIncome && x.Amount > 0));

            return result.Select(x => x.Map<Data.Entities.Transaction, Transaction>());
        }
    }
}